For any nontrivial application, you will have multiple clients
talking to the Azure Table service at the same time. Concurrency becomes
critical, since multiple clients could easily wind up trampling all over
each other’s changes. Windows Azure Table service uses optimistic concurrency to deal with this issue.Each entity has an associated version number that is modified by
the Azure Table service on every update.
When an entity is retrieved, the server sends this version to
the client as an HTTP ETag.
When the client sends an update request to the Table service, it
sends this ETag to the server as an
If-Match header. This happens
automatically with ADO.NET Data Services under the hood.
If the version of the entity on the server is the same as the
ETag in the If-Match header, the change is accepted, and
the entity stored gets a new version on the server. The new version is
returned to the client as an ETag
header.
If the version of the entity on the server is different from the
ETag in the If-Match header, the change is rejected, and
a “precondition failed” HTTP error is returned to the client.
Figure 1 shows an example in which two
clients are trying to update the same entity.
Client 1 updates the entity after Client 2 has retrieved a copy, so
Client 2’s update fails because it doesn’t match the newest ETag.
How do you deal with this error? Well, the good news is that ADO.NET
Data Services sends the right ETag
headers if you set DataServiceContext.MergeOption to MergeOption.PreserveChanges. When the
server sends down a “precondition failed” error, it gets wrapped in a
DataServiceRequestException, which you
can catch and check for by using DataServiceRequestException.Response.First().StatusCode.
What if you don’t care if you have the latest update on the client
side, and you just want your last write to succeed? This is common in
performance-critical scenarios where some data loss is tolerated. In these
cases, you don’t want to retrieve the latest version of the object before
updating the entity.
Doing so is slightly obtuse using ADO.NET Data Services because this
support isn’t as well “baked in” and, frankly, feels slightly weird. You
essentially must send a "*" as the
ETag from the client to the server.
This basically tells the server, “Hey, I don’t care what version you have,
just accept these changes.” Doing so with ADO.NET Data Services involves
detaching the object from the data context, attaching it back with the
wildcard "*" ETag, and then calling
UpdateObject.
The following code snippet shows this:
// set the merge option to overwrite to allow the tracked entity to be updated
context.Detach(entity);
// Attach the entity to the context using the table name, the entity to
// update, and "*" as the ETag value to use.
context.AttachTo("MyTable", entity, "*");
entity.Details = "This write will always succeed";
try
{
context.UpdateObject(entity);
DataServiceResponse response = context.SaveChanges();
}
catch (DataServiceRequestException e)
{
// Error handling - but it cannot throw a PreCondition failure
}